-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix: Restrict block insert, move, and replace attending to allowedBlocks, locking, and child blocks #14003
Conversation
98a3794
to
16bd0e7
Compare
16bd0e7
to
7cd4cac
Compare
7cd4cac
to
fd4a3b1
Compare
eff4d12
to
73cb181
Compare
73cb181
to
e113599
Compare
first( clientIds ) | ||
); | ||
// Replace is valid if the new blocks can be inserted in the root block | ||
// or if we had a block of the same type in the position of the block being replaced. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or if we had a block of the same type in the position of the block being replaced.
Is there an occasion of this you had in mind that led you to implement this exception? It's not clear to me for what reason we'd want it, and it adds some complexity to the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @aduth,
Initially, I followed your suggestion, but on a second thought, I think the condition has some benefits for the merging of blocks.
In the following gist we see a sample block with InnerBlocks:
https://gist.github.com/jorgefilipecosta/6d8c1620b46d916d8a16a23e730a2ab6
If we have this condition the user can merge paragraphs by pressing backspace or delete. Without this condition, merge is not possible. But the same result is possible to achieve by removing a paragraph and appending its content to the content of another paragraph -- So I think merging should be possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the following gist we see a sample block with InnerBlocks:
https://gist.github.com/jorgefilipecosta/6d8c1620b46d916d8a16a23e730a2ab6
This seems like an invalid use of InnerBlocks, by the fact the template contains blocks not valid per allowedBlocks
. At the very least, we shouldn't optimize to anticipate this use-case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @aduth there are InnerBlocks scenarios where I expect this situation to happen.
For example, I just want to allow a maximum of 3 headings inside my InnerBlocks area. The way to implement this restriction would be remove heading from the allowed blocks if we already have 3 headings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I agree even this last scenario I referred is not normal and maybe something we should not optimize for.
I thought replacements would be needed to paste a block in the same position as an existing block. Paste HTML heading in a heading block, but that's not the case. I will reflect a little bit to make sure I'm not missing anything but I find don't any "normal" case I will simplify the logic.
e113599
to
0f15475
Compare
0f15475
to
f30c84a
Compare
This PR was updated after the PR's it depended on were merged and is ready for a new look. |
@@ -201,16 +220,41 @@ export function toggleSelection( isSelectionEnabled = true ) { | |||
* | |||
* @param {(string|string[])} clientIds Block client ID(s) to replace. | |||
* @param {(Object|Object[])} blocks Replacement block(s). | |||
* | |||
* @return {Object} Action object. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yields
is available as a valid substitute in JSDoc.
first( clientIds ) | ||
); | ||
// Replace is valid if the new blocks can be inserted in the root block | ||
// or if we had a block of the same type in the position of the block being replaced. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the following gist we see a sample block with InnerBlocks:
https://gist.github.com/jorgefilipecosta/6d8c1620b46d916d8a16a23e730a2ab6
This seems like an invalid use of InnerBlocks, by the fact the template contains blocks not valid per allowedBlocks
. At the very least, we shouldn't optimize to anticipate this use-case.
|
||
// If moving to other parent block, the move is possible if we can insert a block of the same type inside the new parent block. | ||
if ( canInsertBlock ) { | ||
return action; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we yield
, not return
? Unclear to me what behavior is expected from redux-routine
for the return
.
blocks = castArray( blocks ); | ||
const allowedBlocks = []; | ||
for ( const block of blocks ) { | ||
if ( block ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are there falsey entries in blocks
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it is never falsey, I updated the code to remove this check.
f30c84a
to
cb87da3
Compare
Hi @aduth thank you for the review, I updated the code to follow your suggestions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few questions, but functionally this works well in my testing.
Your "fixes" syntax from the original comment will not auto-close all the related issues. It would need to be separate individual "Fixes" one-per-line.
type: 'MOVE_BLOCK_TO_POSITION', | ||
fromRootClientId, | ||
toRootClientId, | ||
clientId, | ||
index, | ||
}; | ||
// If moving inside the same root block the move is always possible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This too seems contrary in the same sense as #14003 (comment) , and it could produce simpler code to avoid specialized conditions. I could see an argument being made that this helps as an optimized majority case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @aduth although this could be seen as optimization as the majority of cases will return here, this is also required.
The case this covers is locking=insert, in that case, we can not insert blocks but we can move the blocks, canInsertBlockType will return false but the move is in fact possible. Without this condition when locking = insert we would not be able to move the blocks as expected.
fromRootClientId | ||
); | ||
|
||
// If locking is equal to all on the original clientId (fromRootClientId), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Locking is checked in canInsertBlockType
, considered later in the function. Do we need to duplicate the check here as well, or could this condition be removed and it work just as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we need this condition -- If the user is moving the block from a parent with locking=all to a block without locking, canInsertBlockType will return true, but the move is not possible.
* in actions which may result in no blocks remaining in the editor (removal, | ||
* replacement, etc). | ||
*/ | ||
function* ensureDefaultBlock() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given #14754 and the possibility we may want to try to find a different way to "ensure" the default block, I think it's wise to not expose this as an exported action (i.e. as-is).
cb87da3
to
1252d77
Compare
Thank you for the reviews, if there is anything I missed feel free to comment and I will iterate on this. |
…cks, locking, and child blocks (#14003) Supersedes: https://github.com/WordPress/gutenberg/pull/7301/files Uses the select control implemented in #13924. This PR changes the action creators to be a generator and have all the required validations before yielding the action. Fixes: #14568; Fixes: #10186; Fixes: #13099; Missing the update to the unit tests. ## How has this been tested? Test block: [gist.github.com/jorgefilipecosta/b7194daac3ce26827522694fb4252c1c#file-testallowedblocksmiddleware-js](https://gist.github.com/jorgefilipecosta/b7194daac3ce26827522694fb4252c1c#file-testallowedblocksmiddleware-js) I pasted the test block referred in the browser console. I added the Product block. I verified I cannot move using drag & drop the Buy button out of the Product block. (child block restriction. I verified I cannot move using drag & drop a paragraph created outside of the Product block inside the product block. (allowed blocks restriction). I verified that if I press enter in the buy button or image inside the product block, a paragraph is not created (allowed blocks restriction). I added two galleries outside the product block, multi-selected them, and copied them ( ctrl + c ), I pasted them inside the list block inside the Product block and I verified they were not added (allowed blocks restriction). I verified that when we have a template lock and we press enter on the title we now do not insert unallowed blocks. Template lock code: ``` add_filter( 'allowed_block_types', function( $allowed_block_types, $post ) { if ( $post->post_type === 'post' ) { return $allowed_block_types; } return [ 'core/image', 'core/button' ]; }, 10, 2 ); ``` I verified that if we disable the paragraph, e.g., with a filter or inside the test block, pressing enter on the title or in placeholders does not add paragraph blocks. Pressing the sibling inserter also does not insert the paragraph (PR #7226). Code to disable paragraph: ``` function myplugin_register_book_lock_post_type() { $args = array( 'public' => true, 'template_lock' => 'all', 'label' => 'Books Lock', 'show_in_rest' => true, 'template' => array( array( 'core/image', array( ) ), array( 'core/heading', array( 'placeholder' => 'Add Author...', ) ), array( 'core/paragraph', array( 'placeholder' => 'Add Description...', ) ), ), ); register_post_type( 'book_lock', $args ); } add_action( 'init', 'myplugin_register_book_lock_post_type' ); ``` I added two image blocks, multi-selected them, copied them and tried to paste them on the list inside the test block. I checked the image blocks were not added inside the test block. All these actions are possible in master. Todo: If we accept this approach, end 2 end tests will be created before the merge that replicate this manual tests.
…cks, locking, and child blocks (#14003) Supersedes: https://github.com/WordPress/gutenberg/pull/7301/files Uses the select control implemented in #13924. This PR changes the action creators to be a generator and have all the required validations before yielding the action. Fixes: #14568; Fixes: #10186; Fixes: #13099; Missing the update to the unit tests. ## How has this been tested? Test block: [gist.github.com/jorgefilipecosta/b7194daac3ce26827522694fb4252c1c#file-testallowedblocksmiddleware-js](https://gist.github.com/jorgefilipecosta/b7194daac3ce26827522694fb4252c1c#file-testallowedblocksmiddleware-js) I pasted the test block referred in the browser console. I added the Product block. I verified I cannot move using drag & drop the Buy button out of the Product block. (child block restriction. I verified I cannot move using drag & drop a paragraph created outside of the Product block inside the product block. (allowed blocks restriction). I verified that if I press enter in the buy button or image inside the product block, a paragraph is not created (allowed blocks restriction). I added two galleries outside the product block, multi-selected them, and copied them ( ctrl + c ), I pasted them inside the list block inside the Product block and I verified they were not added (allowed blocks restriction). I verified that when we have a template lock and we press enter on the title we now do not insert unallowed blocks. Template lock code: ``` add_filter( 'allowed_block_types', function( $allowed_block_types, $post ) { if ( $post->post_type === 'post' ) { return $allowed_block_types; } return [ 'core/image', 'core/button' ]; }, 10, 2 ); ``` I verified that if we disable the paragraph, e.g., with a filter or inside the test block, pressing enter on the title or in placeholders does not add paragraph blocks. Pressing the sibling inserter also does not insert the paragraph (PR #7226). Code to disable paragraph: ``` function myplugin_register_book_lock_post_type() { $args = array( 'public' => true, 'template_lock' => 'all', 'label' => 'Books Lock', 'show_in_rest' => true, 'template' => array( array( 'core/image', array( ) ), array( 'core/heading', array( 'placeholder' => 'Add Author...', ) ), array( 'core/paragraph', array( 'placeholder' => 'Add Description...', ) ), ), ); register_post_type( 'book_lock', $args ); } add_action( 'init', 'myplugin_register_book_lock_post_type' ); ``` I added two image blocks, multi-selected them, copied them and tried to paste them on the list inside the test block. I checked the image blocks were not added inside the test block. All these actions are possible in master. Todo: If we accept this approach, end 2 end tests will be created before the merge that replicate this manual tests.
Supersedes: https://github.com/WordPress/gutenberg/pull/7301/files
Uses the select control implemented in #13924.
This PR changes the action creators to be a generator and have all the required validations before yielding the action.
Fixes: #14568;
Fixes: #10186;
Fixes: #13099;
Missing the update to the unit tests.
How has this been tested?
Test block: gist.github.com/jorgefilipecosta/b7194daac3ce26827522694fb4252c1c#file-testallowedblocksmiddleware-js
I pasted the test block referred in the browser console.
I added the Product block.
I verified I cannot move using drag & drop the Buy button out of the Product block. (child block restriction.
I verified I cannot move using drag & drop a paragraph created outside of the Product block inside the product block. (allowed blocks restriction).
I verified that if I press enter in the buy button or image inside the product block, a paragraph is not created (allowed blocks restriction).
I added two galleries outside the product block, multi-selected them, and copied them ( ctrl + c ), I pasted them inside the list block inside the Product block and I verified they were not added (allowed blocks restriction).
I verified that when we have a template lock and we press enter on the title we now do not insert unallowed blocks.
Template lock code:
I verified that if we disable the paragraph, e.g., with a filter or inside the test block, pressing enter on the title or in placeholders does not add paragraph blocks. Pressing the sibling inserter also does not insert the paragraph (PR #7226).
Code to disable paragraph:
I added two image blocks, multi-selected them, copied them and tried to paste them on the list inside the test block. I checked the image blocks were not added inside the test block.
All these actions are possible in master.
Todo: If we accept this approach, end 2 end tests will be created before the merge that replicate this manual tests.